package sintatico;

import java.util.ArrayList;
import java.util.List;
import token.Token;
import token.TokenAll;
import token.TokenType;

/**
 * ANALISADOR SINTATICO
 *
 */
public class Sintatico {
    /**
     * Lista de Tokens recebida do lexico
     */
    private List<Token> tokens;
    /**
     * Lista de erros gerada a partir da analise sintática
     */
    private List<String> erros;
    /**
     * Ponteiro da análise sintática,
     * todos os metodos ao chamarem outros métodos devem posicionar o ponteiro onde o outro método irá iniciar a leitura
     * todos os metodos ao terminarem devem deixar o ponteiro uma posição deipos do que foi reconhecido por este
     * Em caso de erro o ponteiro aponta para a psição onde ocorreu o erro
     */
    private int p;
    /**
     * Referência para o token atual
     */
    private Token t;
    /**
     * Construtor
     */
    public Sintatico(){
        erros = new ArrayList<String>();
        p = 0;
    }
    public void analiseTokens(List<Token> tokens) {
        this.tokens = tokens;
        t = tokens.get(p);
        this.algoritmo();
    }

    public List<String> getErros() {
        return erros;
    }

    //##########################################################################
    //###PRODUÇÕES PRIMITIVAS
    //##########################################################################

    /**by: fernando
     * <OPERADOR_BOOLEANO> ::= “&&” | “||”
     */
    private boolean booleano(){
        if(t.getTipo() == TokenAll.verdadeiro || t.getTipo() == TokenAll.falso ){
            nextToken();
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado verdadeiro ou falso ");
            return false;
        }
    }
    private boolean caractere(){
        return true;
    }
    private boolean string(){
        return true;
    }
    private boolean Char(){
        return true;
    }
    /**by: jhielson
     *
     <NUMERO> ::= '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'
     */
    private boolean numero(){
        if(t.getTipo() == TokenAll.NUMERO){
            nextToken();
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um número.");
            sinc(new TokenAll[]{});
            return false;
        }
    }
    /**by: fernando
     * <OPERADOR_IGUALDADE> ::= “==” | “!=”
     */
    private boolean operador_igualdade(){
        if(t.getTipo() == TokenAll.IGUALDADE || t.getTipo() == TokenAll.DIFERENCA ){
            nextToken();
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado == ou != ");
            return false;
        }
    }
    /**by: fernando
     * <OPERADOR_RELACIONAL> ::= ">" | ”>=” | "<" | ”<=”|<OPERADOR_IGUALDADE>
     */
    private boolean operador_relacional(){
        if(t.getTipo() == TokenAll.MAIOR || t.getTipo() == TokenAll.MENOR ||
                t.getTipo() == TokenAll.MAIOR_IGUAL || t.getTipo() == TokenAll.MENOR_IGUAL
                || operador_igualdade()){
            nextToken();
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado <, <=, < ou <= ");
            return false;
        }
    }
    /**by: fernando
     * <OPERADOR_BOOLEANO> ::= “&&” | “||”
     */
    private boolean operador_booleano(){
        if(t.getTipo() == TokenAll.E || t.getTipo() == TokenAll.OU ){
            nextToken();
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado && ou || ");
            return false;
        }
    }
    /**by: fernando
     * <OPERADOR_INC> ::= “++” | “--”
     */
    private boolean operador_inc(){
        if(t.getTipo() == TokenAll.INCREMENTO || t.getTipo() == TokenAll.DECREMENTO ){
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado ++ ou -- ");
            return false;
        }
    }

    /**by: Thay
     * <TIPO_PROMITIVO> ::= “inteiro” | “real” | “caractere”
     */
    private boolean tipo_primitivo(){
        if(t.getTipo() == TokenAll.inteiro || t.getTipo() == TokenAll.real
                || t.getTipo() == TokenAll.caractere){
            return true;
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado tipo primitivo 'inteiro' ou 'real' ou 'caractere'");
            return false;
        }
    }
    private boolean identificador(){
        return true;
    }

    //##########################################################################
    //### METODOS PARA OS NÃO TERMINAIS
    //##########################################################################

    //##########################################################################
    //### INICIO DO PROGRAMA
    //##########################################################################

    /**
     * Faz a identificação da linha de definição do algoritmo
     * Chama a identificação do código
     * BY: Fabricio
     */
    private void algoritmo(){
        System.out.println("algoritmo");

        t = tokens.get(p);

        if(t.getTipo() == TokenAll.algoritmo){
            //p++;
            //t = tokens.get(p);
            nextToken();
            if(t.getTipo() == TokenAll.IDENTIFICADOR){
                //p++;
                nextToken();
                codigo();
            }
            else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado identificador para o algoritmo");
                sinc(new TokenAll[]{});
            }
        }
        else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado palavra reservada >> algoritmo");
            sinc(new TokenAll[]{});
        }
    }
    /**
     * by Ciro Marx
     *
     * Faz o reconhecimento do corpo de todo o algoritmo
     * Chama metodos especificos para reconhecimento de:
     * - Declaração de variáveis globais
     * - Declaração de funções
     * - Declaração de tipos e registros
     */
    private boolean codigo()
    {
        System.out.println("Entrou em codigo");
        boolean retorno = true;

        while(this.t.getTipo() != TokenAll.EOF)
        {
            if(!this.declaracoes())
                retorno = false;
        }
        return retorno;
    }
    /**
     * by Ciro Marx - Estou fazendo...
     *
     */
    private boolean declaracoes()
    {
        System.out.println("Entrou em declarações");
        boolean retorno = true;

        if(this.t.getTipo() == TokenAll.tipo)
            return this.tipo();
        else
            if(this.t.getTipo() == TokenAll.constantes)
                return this.constante();
            else
                if(this.t.getTipo() == TokenAll.variaveis)
                    return this.variavel();
                else
                    if(this.t.getTipo() == TokenAll.funcao)
                        return this.funcao();
                    else
                    {
                        erros.add("ERRO - Linha: " + t.getLinha()+" Token: "+this.t.getTipo().toString()+"- Início de declaração inválida");
                        retorno = false;
                        this.sinc(sincronizadores);
                    }

        return retorno;
    }

    private boolean tipo(){
        System.out.println("Entrou em tipo()");
        return true;
    }
    private boolean registro(){
        System.out.println("Entrou em registro()");
        return true;
    }
/**
     * by JC e Thay
     *
     * <CONSTANTE> ::= “constantes” <TIPO_PRIMITIVO> <ID> “= “ <EXPRESSOES>“;”
     */
    private boolean constante(){
        System.out.println("Entrou em constante()");

        if(t.getTipo() == TokenAll.constantes){
            this.nextToken();
        } else {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperava 'constantes'");
        }
        if(t.getTipo() == TokenAll.caractere | t.getTipo() == TokenAll.inteiro |
                t.getTipo() == TokenAll.real | t.getTipo() == TokenAll.constantes) {
            this.nextToken();
        } else {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperava uma declaração de tipo primitivo");
            sinc(new TokenAll[]{});
            return false;
        }
        if(t.getTipo() == TokenAll.IDENTIFICADOR) {
            this.nextToken();
        } else {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperava um identificador.");
        }
        if(t.getTipo() == TokenAll.ATRIBUICAO) {
            this.nextToken();
        } else {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperava o símbolo '='.");
        }
        if(this.expressoes()) {
            this.nextToken();
        } else {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Expressão iválida!");
        }

        return true;
    }
    private boolean variavel(){
        System.out.println("Entrou em variavel()");
        return true;
    }
    private boolean array(){
        System.out.println("Entrou em array()");
        return true;
    }

    private boolean acesso(){
        boolean retorno = true;

        if(t.getTipo() == TokenAll.IDENTIFICADOR){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado identificador para o algoritmo");
            retorno = false;
            sinc(new TokenAll[]{});
        }//fim do if ID



        return true;
    }

    //##########################################################################
    //### FUNÇÕES
    //##########################################################################

    private boolean funcao(){
        return true;
    }
    private boolean corpo_funcao(){
        return true;
    }
    private boolean c_f_vazio(){
        return true;
    }
    private boolean c_f_comum(){
        return true;
    }
    private boolean l_param(){
        return true;
    }
    private boolean chamadaDeFuncao(){
        return true;
    }
    private boolean l_arg(){
        return true;
    }
    /**
     * by Ciro Marx
     *
     *<BLOCO> ::= "{" {<COMANDO>} "}"
     */
    private boolean bloco(){
        System.out.println("Entrou em bloco");
        boolean retorno = true;
        if(this.t.getTipo() != TokenAll.ABRECHAVES)
        {
            erros.add("ERRO - Linha " + t.getLinha()+ ": "+" Esperado '{'");
            retorno = false;
        }
        this.nextToken();
        //Verifica se os tokens são comandos validos até encontrar "}" ou EOF
        while(this.t.getTipo() != TokenAll.FECHACHAVES && this.t.getTipo() != TokenAll.EOF)
            this.comando();
        //Verifica se o ultimo token foi um "}"
        if(this.t.getTipo() != TokenAll.FECHACHAVES)
        {
            erros.add("ERRO - Linha " + t.getLinha()+ ": "+"'}' não encontrado");
            retorno = false;
        }
        this.nextToken();
        return retorno;

    }
    /**
     * by Ciro Marx
     *
     * <COMANDO>  ::= [ <SE> | <ENQUANTO> | <PARA> | <VARIAVEL> | <CHAMADA_FUNCAO> ";" | <RETORNO>
     * | <LEIA> | <ESCREVA> | <CHAMADA_FUNCAO> | <ATRIBUICAO>]
     *
     * A cada nível testará uma das possíveis produções. Caso o teste dê verdadeiro em algum nível, chamará
     * o não terminal correspondente, não prosseguindo com os testes.
     *
     */
    private boolean comando(){
        if(this.t.getTipo() == TokenAll.se)         //Tenta produzir o "se"
            return this.se();
        else
            if(this.t.getTipo() == TokenAll.enquanto) //Tenta produzir o "enquanto"
                return this.enquanto();
            else
                if(this.t.getTipo() == TokenAll.para) //Tenta produzir o "para"
                    return this.para();
                else
                    if(this.t.getTipo() == TokenAll.variaveis) //Tenta produzir declaração de variavel
                        return this.variavel();
                    else
                        if(this.t.getTipo() == TokenAll.retorno) // Tenta produzir o "retorno"
                            return this.retorno();
                        else
                            if(this.t.getTipo() == TokenAll.leia) //Tenta produzir o "leia"
                                return this.leia();
                            else
                                if(this.t.getTipo() == TokenAll.escreva) //Tenta produzir o "escreva"
                                    return this.escreva();
                                else
                                    if(this.t.geteToke() == TokenType.IDENTIFICADOR)  //Caso seja o token identificador, existem duas possibilidades
                                    {
                                        if(this.tokens.get(p+1).getTipo()== TokenAll.ABREPARENTESES) //Se o token seguinte for "(", então tentará
                                            return this.chamadaDeFuncao();                                  //produzir uma chamada de função
                                        else                            //Caso contrário, tentará produzir
                                            return this.atribuicao();          //uma "atribuição"
                                    }
                                    else                                        //Se todos os testes anteriores falharam, não é um comando válido
                                    {
                                        erros.add("ERRO - Linha " + t.getLinha()+ ": " + this.t.getValor() + "não é um comando válido");
                                        // O que colocar como sincronizador?
                                        this.nextToken();
                                        return false;
                                    }
    }

    //##########################################################################
    //### COMANDOS
    //##########################################################################

    private boolean tipo_var(){
        return true;
    }
    /**By: João Gabriel.
     *
     * Faz o reconhecimento das expressões de atribuição no código.
     * Chama os métodos:
     * - sinc()
     * - expressoes()
     * @return
     * Retorna true se a atribuição foi implementada corretamente e
     * false caso contrário.
     */
    private boolean atribuicao(){
        System.out.println("ENTROU - atribuicao");
        boolean retorno = true;
        /*
         * No if, caso o comando esteja correto o ponteiro é incrementado e
         * "t" recebe o próximo token. Se não estiver correto é impressa uma
         * mensagem de erro, o retorno do método é modificado e o método de
         * sincronização é chamado.
         */
        if(t.getTipo() == TokenAll.IDENTIFICADOR){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> IDENTIFICADOR");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if IDENTIFICADOR

        if(t.getTipo() == TokenAll.ATRIBUICAO){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> =");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if ATRIBUICAO

        if(this.expressoes()){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> EXPRESSOES");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if expressoes()

        System.out.println("SAIU - atribuicao");
        return retorno;
    }//fim do método atribuicao

    /** By: João Gabriel.
     * Metodo criado para fazer o reconhecimento do comando "enquanto" no
     * código criado no 333Compiler.
     * @return
     * Retorna true se o código foi escrito corretamente e false caso contrário.
     */
    private boolean enquanto(){
        boolean retorno = true;

        System.out.println("ENTROU - enquanto.");
        if(t.getTipo() == TokenAll.enquanto){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado >> enquanto");
            retorno = false;
            sinc(new TokenAll[]{});
        }//fim do if enquanto

        if(t.getTipo() == TokenAll.ABREPARENTESES){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado >> (");
            retorno = false;
            sinc(new TokenAll[]{});
        }//fim do if abre parenteses

        if(this.expressao_relacional()){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado >> Expressão Relacional");
            retorno = false;
            sinc(new TokenAll[]{});
        }//fim do if expressão relacional

        if(t.getTipo() == TokenAll.FECHAPARENTESES){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado >> )");
            retorno = false;
            sinc(new TokenAll[]{});
        }//fim do if fecha parenteses

        if(this.bloco()){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado >> Bloco");
            retorno = false;
            sinc(new TokenAll[]{});
        }//fim do if bloco.

        System.out.println("SAIU - enquanto.");
        return retorno;
    }//fim do método enquanto

    /**
     * by Ciro Marx
     *
     * <SE> ::= “se” ”(“ <EXPRESSAO_RELACIONAL> ”)”  “entao” <BLOCO> <SENAO>
     */
    private boolean se(){
        System.out.println("Entrou no 'se'");
        boolean retorno = true;
        if(this.t.getTipo()!=TokenAll.se)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um 'se'");
            retorno = false;
        }
        nextToken();

        if(this.t.getTipo()!=TokenAll.ABREPARENTESES)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um '('");
            retorno = false;
        }
        nextToken();

        if(!this.expressao_relacional())
        {
            this.sinc(TokenAll.FECHAPARENTESES);
            retorno = false;
        }

        if(this.t.getTipo()!=TokenAll.FECHAPARENTESES)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um ')'");
            retorno = false;
            this.sinc(TokenAll.entao);
        }
        else
            nextToken();

        if(this.t.getTipo()!=TokenAll.entao)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um 'entao'");
            retorno = false;
            this.sinc(TokenAll.ABRECHAVES);
        }
        else
            nextToken();

        if(!this.bloco())
            retorno = false;
        if(!senao())
            retorno = false;

        return retorno;
    }
    /**
     * by Ciro Marx
     *
     *<SENAO> ::= “senao” <BLOCO>
     */
    private boolean senao(){
        boolean retorno = true;
        if(this.t.getTipo() == TokenAll.senao)
        {
            this.nextToken();
            if(!this.bloco())
                retorno = false;
        }
        return retorno;
    }
    /**
     * by Ciro Marx
     *
     *<ESCREVA> ::= “escreva” “(“ { <TERMO_STRING>|<TERMO_STRING> “+” } “)” ;
     */
    private boolean escreva()
    {
        System.out.println("Entrou no escreva");
        boolean retorno = true;
        if(this.t.getTipo() != TokenAll.escreva)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um 'escreva'");
            retorno = false;
        }
        this.nextToken();
        System.out.println("Passou do 1...");

        if(this.t.getTipo() != TokenAll.ABREPARENTESES)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um '('");
            retorno = false;
            TokenAll[] sincro = new TokenAll[]{TokenAll.STRING, TokenAll.IDENTIFICADOR, TokenAll.CHAR};
            this.sinc(sincro);
        }
        else
            this.nextToken();
        System.out.println("Passou do 2...");

        while(this.termo_string())
        {
            if(this.t.getTipo()== TokenAll.SOMA)
                this.nextToken();
        }
        System.out.println("Passou do 3...");

        if(!this.termo_string())
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado uma String");
            retorno = false;
            this.sinc(TokenAll.FECHAPARENTESES);
        }

        System.out.println("Passou do 4...");

        if(this.t.getTipo() != TokenAll.FECHAPARENTESES)
        {
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um ')'");
            retorno = false;
            //this.sinc(TokenAll.PONTOEVIRGULA);
        }
        this.nextToken();

        System.out.println("Passou do 5...");
        System.out.println(this.t.getTipo().toString());

        if(this.t.getTipo() != TokenAll.PONTOEVIRGULA)
        {
            System.out.println("Óia");
            erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um ';'");
            retorno = false;
            //this.sinc(TokenAll.PONTOEVIRGULA);
        }
        this.nextToken();

        return retorno;
    }
    /**
     * by Ciro Marx
     *
     *<TERMO_STRING> ::= <STRING>|<CHAR>|<CHAMADA_FUNCAO>|<IDENTIFICADOR>
     */
    private boolean termo_string()
    {
        boolean retorno = true;
        System.out.println(this.t.getValor());
        if(this.t.getTipo() == TokenAll.STRING || this.t.getTipo() == TokenAll.CHAR || this.t.getTipo() == TokenAll.IDENTIFICADOR)
        {
            this.nextToken();
            System.out.println("Entrou aqui!");
        }
        else
            if(!this.chamadaDeFuncao())
            {
                erros.add("ERRO - Linha: " + t.getLinha() + "- Esperado um ';'");
                retorno = false;
            }

        return retorno;
    }
    private boolean leia(){
        System.out.println("Entrou em 'leia'");
        return true;
    }
    /**By: João Gabriel.
     *
     * Faz o reconhecimento do comando "retorno" no código.
     * Chama os métodos:
     * - sinc()
     * - expressoes()
     * @return
     * Retorna true se o comando "retorno" fi implementado corretamente
     * e false caso contrário.
     */
    private boolean retorno(){
        System.out.println("ENTROU - retorno");
        boolean retorno = true;
        /*
         * No if, caso o comando esteja correto o ponteiro é incrementado e
         * "t" recebe o próximo token. Se não estiver correto é impressa uma
         * mensagem de erro, o retorno do método é modificado e o método de
         * sincronização é chamado.
         */
        if(t.getTipo() == TokenAll.retorno){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando palavra reservada >> retorno");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if "retorno"

        if(this.expressoes()){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> EXPRESSOES");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if expressoes()

        if(t.getTipo() == TokenAll.PONTOEVIRGULA){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> ;");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if ""ponto e vírgula"

        System.out.println("SAIU - retorno");
        return retorno;
    }//fim do método retorno()
    /**By: João Gabriel.
     *
     * Faz o reconhecimento do comando "para" no código.
     * Chama os métodos:
     * - sinc()
     * - ini()
     * - expressaoRelacioal()
     * - expressaoAritmetica()
     * @return
     * Retorna true se o comando "para" esta correto
     * e false caso contrário.
     */
    private boolean para(){
        System.out.println("ENTROU - para");

        boolean retorno = true;
        /*
         * No if, caso o comando esteja correto o ponteiro é incrementado e
         * "t" recebe o próximo token. Se não estiver correto é impressa uma
         * mensagem de erro, o retorno do método é modificado e o método de
         * sincronização é chamado.
         */
        if(retorno){
            if(t.getTipo() == TokenAll.para){
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando palavra reservada >> para");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if "para".

            if(t.getTipo() == TokenAll.ABREPARENTESES){
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> (");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if "(".

            if(this.ini()){
                //this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() +
                        " - Esperando >> atribuição tipo: id = numero | expressão | função");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if atribuição

            if(t.getTipo() == TokenAll.PONTOEVIRGULA){
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> ;");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if ""ponto e vírgula"

            if(this.expressao_relacional()){
                System.out.println("ER aqui!!");
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> EXPRESSÃO RELACIONAL");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if expressão relacional

            if(t.getTipo() == TokenAll.PONTOEVIRGULA){
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> ;");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if ""ponto e vírgula"

            if(this.expressaoAritmetica()){
                System.out.println("EA aqui!!");
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> EXPRESSÃO ARITMÉTICA");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if "expressão aritmética"

            if(t.getTipo() == TokenAll.FECHAPARENTESES){
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> )");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if "fecha parênteses"
/*
            if(this.bloco()){
                this.nextToken();
            }else{
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> BLOCO");
                retorno = false;
                this.sinc(new TokenAll[]{});
            }//fim do if bloco.
 *
 */
        }//fim do if "retorno"

        System.out.println("SAIU - para");
        return retorno;
    }//fim do método para()

    /**By: João Gabriel.
     *
     * Caso particular do método "atribuição" criado para auxiliar na implementação
     * do método "para". Verifica apenas identificadores, inteiros e funções.
     * @return
     * Retorna true se a expressão "ini" estiver correta e false
     * caso contrário.
     */
    private boolean ini(){
        System.out.println("ENTROU - ini");
        boolean retorno = true;
        /*
         * No if, caso o comando esteja correto o ponteiro é incrementado e
         * "t" recebe o próximo token. Se não estiver correto é impressa uma
         * mensagem de erro, o retorno do método é modificado e o método de
         * sincronização é chamado.
         */
        if(t.getTipo() == TokenAll.IDENTIFICADOR){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> IDENTIFICADOR");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if IDENTIFICADOR

        if(t.getTipo() == TokenAll.ATRIBUICAO){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> =");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if ATRIBUICAO

        if(t.getTipo() == TokenAll.NUMERO || t.getTipo() == TokenAll.IDENTIFICADOR
                || this.chamadaDeFuncao()){
            this.nextToken();
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperando >> " +
                    "INTEIRO ou IDENTIFICADOR ou FUNÇÃO");
            retorno = false;
            this.sinc(new TokenAll[]{});
        }//fim do if ini.

        System.out.println("SAIU - ini");
        return retorno;
    }//fim do método ini
    private boolean ini2(){
        return true;
    }

    //##########################################################################
    //### EXPRESSOES
    //##########################################################################
    /* *by: fernando
     *
     */
    private boolean expressoes(){
        return true;
    }
    /* *by: fernando
     * <EXPRESSAO_RELACIONAL> ::= <EXPRESSAO_LOGICA>[<OPERADOR_IGUALDADE>
     * <EXPRESSAO_LOGICA>] | <EXPRESSAO_ARITMETICA><OPERADOR_RELACIONAL>
     * <EXPRESSAO_ARITMETICA>
     */
    private boolean expressao_relacional(){
        if(expressao_logica()){
            nextToken();
            if(operador_igualdade()){
                nextToken();
                if(expressao_logica()){
                    nextToken();
                    return true;
                }else{
                    return false;
                }
            }else{
                return true;
            }
        }else if (expressaoAritmetica()){
            nextToken();
            if(operador_relacional()){
                nextToken();
                if(expressaoAritmetica()){
                    nextToken();
                    return true;
                }else{
                    return false;
                }
            }else{
                return false;
            }
        }else{
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado uma expressao ");
            sinc(new TokenAll[]{});
            return false;
        }
        //return true;
    }

    private boolean expressao_logica(){
        return true;
    }
    private boolean expressaoAritmetica(){
        //TODO
        //Token t = tokens.get(p);

        if(!termo()){
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado termo.");
            sinc(new TokenAll[]{});
            return false;
        }

        while(t.getTipo() == TokenAll.SOMA || t.getTipo() == TokenAll.DIVISAO)
        {
            nextToken();
            if(!termo()){
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado termo.");
                sinc(new TokenAll[]{});
                return false;
            }
        }
        return true;
    }
    /**
     *
    <TERMO> ::= <CAST> { ( “*” <CAST> ) | ( “/” <CAST> ) }
     */
    private boolean termo(){
        //Token t = tokens.get(p);
        if(!cast()){
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado cast.");
            sinc(new TokenAll[]{});
            return false;
        }

        while(t.getTipo() == TokenAll.MULTIPLICACAO || t.getTipo() == TokenAll.DIVISAO)
        {
            //p++;
            //t = tokens.get(p);
            nextToken();
            if(!cast()){
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado cast.");
                sinc(new TokenAll[]{});
                return false;
            }
        }
        return true;
    }
    /**
     *
    <CAST> ::= [ “(“ <TIPO_PRIMITIVO> “)” ] <INCREMENTO>
     */
    private boolean cast(){
        //Token t = tokens.get(p);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//           TIREI O CAST PQ ESTÁ GERANDO AMBIGUIDADE (CORRIGIR)
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//        if(t.getTipo() == TokenAll.ABREPARENTESES){
//            //p++;
//            //t = tokens.get(p);
//            nextToken();
//            if(tipoPrimitivo())
//            {
//                //p++;
//                //t = tokens.get(p);
//                nextToken();
//                if(t.getTipo() == TokenAll.FECHAPARENTESES){
//                    //p++;
//                    //t = tokens.get(p);
//                    nextToken();
//                }else
//                {
//                    erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um \")\".");
//                    sinc(new TokenAll[]{});
//                    return false;
//                }
//            }else
//            {
//                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um tipo primitivo.");
//                sinc(new TokenAll[]{});
//                return false;
//            }
//        }
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

        if(incremento())
        {
            return true;
        }else
        {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado \"incremento\".");
            sinc(new TokenAll[]{});
            return false;
        }

    }
    /**
     *
    <INCREMENTO> ::= [<OPERADOR_INC>] <FATOR> | <FATOR> [ <OPERADOR_INC>]
     */
    private boolean incremento(){
        if(operador_inc()){
            if(fator())
            {
                return true;
            }else
            {
                erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um fator.");
                sinc(new TokenAll[]{});
                return false;
            }
        }else if(fator())
        {
            if(!operador_inc()){
               System.out.println("XXX");
               return true;
            }
            return false;
        }else
        {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um fator.");
            sinc(new TokenAll[]{});
            return false;
        }
    }
    /**
     *
    <FATOR> ::= <NUMERO> | ”(“ <EXPRESSAO_ARITMETICA> “)” |<CHAMADA_FUNCAO>|<ID>
     */
    private boolean fator(){
        //Token t = tokens.get(p);
        if(numero()){
            return true;
        }else if(t.getTipo() == TokenAll.ABREPARENTESES)
        {
            //p++;
            //t = tokens.get(p);
            nextToken();
            if(expressaoAritmetica())
            {
                //p++;
                //t = tokens.get(p);
                nextToken();
                if(t.getTipo() == TokenAll.FECHAPARENTESES)
                {
                    //p++;
                    nextToken();
                    return true;
                }else
                {
                    erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um \")\"");
                    sinc(new TokenAll[]{});
                    return false;
                }
            }else
            {
                    erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado uma expressão aritmética");
                    sinc(new TokenAll[]{});
                    return false;
            }
        }else if(chamadaDeFuncao())
        {
            return true;
        }else if(identificador())
        {
            return true;
        }else
        {
            erros.add("ERRO - Linha: " + t.getLinha() + " - Esperado um \"(\" ou um número ou uma" +
                    "chamada de função ou um identificador.");
            sinc(new TokenAll[]{});
            return false;
        }
    }


    //##########################################################################
    //### SINCRONIZACAO
    //##########################################################################
    /**
     * Lista com os tokens de sincronismo
     */
    TokenAll[] sincronizadores = {
        TokenAll.ABRECHAVES, TokenAll.PONTOEVIRGULA,
        TokenAll.funcao,
        TokenAll.registro,
        TokenAll.para,
        TokenAll.se,
        TokenAll.senao,
        TokenAll.enquanto,
        TokenAll.escreva,
        TokenAll.leia,
    };

    /**
     * Executa o sincronismo em caso de erro
     * O ponteiro deve apontar para o local onde foi encontrado o problema
     * Procura por um token de sincronismo e chama o método para continuar a análise a partir dele
     */
    private void sinc(TokenAll[] tokenAll) {

        while(t.getTipo() != TokenAll.EOF) {
            for(int i=0; i < tokenAll.length; i++) {
                if(tokenAll[i].equals(t.getTipo())) {
                    return;
                }
            }
            for(int i=0; i < sincronizadores.length; i++) {
                if(sincronizadores[i].equals(t.getTipo())) {
                    switch(t.getTipo()) {
                        case funcao : funcao(); break;
                        case registro : registro(); break;
                        case para : para(); break;
                        case se : se(); break;
                        case senao : senao(); break;
                        case enquanto : enquanto(); break;
                        case escreva : escreva(); break;
                        case leia : leia(); break;
                    }
                    return;
                }
            }
            nextToken();
        }
    }

    private void sinc(TokenAll tokenAll) {
        System.out.println("Entrei no sincronizador: "+tokenAll.toString());
        boolean achei = false;

        while(t.getTipo() != TokenAll.EOF) {
            System.out.println("t: "+t.getTipo().toString());
            if(tokenAll == t.getTipo()) {
                System.out.println("Achei!");
                return;
            }
            for(int i=0; i < sincronizadores.length; i++) {
                if(sincronizadores[i].equals(t.getTipo())) {
                    System.out.println("Entrei no switch...");
                    switch(t.getTipo()) {
                        case funcao : funcao(); break;
                        case registro : registro(); break;
                        case para : para(); break;
                        case se : se(); break;
                        case senao : senao(); break;
                        case enquanto : enquanto(); break;
                        case escreva : escreva(); break;
                        case leia : leia(); break;
                    }
                    return;
                }
            }
            System.out.println("Saí do for...");
            nextToken();
        }
    }
    //##########################################################################
    //### NEXT TOKEN
    //##########################################################################

    /**
     * by Ciro Marx
     * <<<<<    nextToken();    >>>>>>>
     *
     * Função que colocará o próximo token a ser analisado na posição atual, ou seja, na variável 't'. Caso não
     * haja mais tokens na lista, atribuirá a variável 't' um novo token EOF
     *
     */
    private void nextToken()
    {
        if(p < this.tokens.size()-1)              //Se ponteiro é menor que o tamanho da lista
        {
            p++;                                //O ponteiro é incrementado
            t = this.tokens.get(p);
        }
        else
            this.t = new Token(TokenAll.EOF, null,this.t.getLinha(), TokenType.EOF);
    }

}